package model.graphic.objects;

import controller.Constants;
import controller.commands.AddAttachmentCommand;
import controller.commands.AddConnectionCommand;
import controller.commands.DeleteCommand;
import controller.commands.SetHighlightColorCommand;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import model.graphic.locations.FlexibleRelativeLocation;
import model.graphic.locations.Location;
import view.GUI.LabelReader;
import view.GUI.graphic.GraphicView;

/**
 *
 * @author Administrator, modified by Ben
 */
public class GOutput extends AbstractHandle {
    private Line2D l1,  l2, l3;
    private int xdiff = 8;
    private int ydiff = 0;
    private int yp;
    private GSine line;//Line2D line;
    protected Polygon polygon;
    private Color oldColor = Color.RED;
    public static final int ARROW_DELTA = 8;

    public GOutput() {
    }

    public Location getArrowBasePoint(){
        return  new FlexibleRelativeLocation(((RectangleAttachment) base).getUpperRight(), 0, 15 / 2);
    }

    public GOutput(Attachment baseHandle) {
        super(baseHandle, new FlexibleRelativeLocation(((RectangleAttachment) baseHandle).getUpperRight(), 0, 0));
        line = new GSine();//new Line2D.Double();
        l1 = new Line2D.Double(100 + xdiff, 100 + yp, 100 - ARROW_DELTA, 100 - ARROW_DELTA + yp );
        l2 = new Line2D.Double(100 + xdiff, 100 + yp, 100 - ARROW_DELTA, 100 + ARROW_DELTA + yp );
        l3 = new Line2D.Double(100 - ARROW_DELTA, 100 - ARROW_DELTA + yp , 100 - ARROW_DELTA, 100 + ARROW_DELTA + yp);
    }

    public void move(Attachment base) {
        adjustLocation(base, new FlexibleRelativeLocation(((RectangleAttachment) base).getUpperRight(), 0, 0));
    }

    public void paint(Graphics2D g) {
        if (this.isVisible() && (base.isVisible() || ((RectangleAttachment)base).isCollapsed() )) {
            Color oldColor = g.getColor();
            Stroke oldStroke = g.getStroke();

            if (selected) {
                g.setColor(getHighlightColor());
            } else {
                g.setColor(getBaseColor());
            }

            RectangleAttachment ob = ((RectangleAttachment) getBase());
            yp = 15 / 2;
            int xx = ob.getUpperRight().getX() + xdiff;
            int yy = ob.getUpperRight().getY() + ydiff;

            //line = new Line2D.Double(ob.getUpperRight().getX(),ob.location.getY() + yp, xx - ARROW_DELTA, yy + yp);
            line = new GSine(ob.getUpperRight().getX(),ob.location.getY() + yp, xx - ARROW_DELTA, yy + yp);
            l1 = new Line2D.Double(xx, yy + yp+1, xx - ARROW_DELTA, yy - ARROW_DELTA + yp+1);
            l2 = new Line2D.Double(xx, yy + yp+1, xx - ARROW_DELTA+1, yy + ARROW_DELTA + yp);
            l3 = new Line2D.Double(xx - ARROW_DELTA, yy - ARROW_DELTA + yp+1,
                                   xx - ARROW_DELTA, yy + ARROW_DELTA + yp);
            if (highlighted) {
                g.setColor(getHighlightColor());
                g.setStroke(new BasicStroke(2.0f));
            }
            line.paint(g);//g.draw(line);
            g.draw(l1);
            g.draw(l2);
            g.draw(l3);
            g.setColor(oldColor);
            g.setStroke(oldStroke);
        }
    }

    @Override
    public boolean contains(int x, int y) {
        boolean isIn = false;

        if (createPolygon().contains(x + getX(), y + getY())) {
            isIn = true;
        }
        if (createPolygonFork().contains(x + getX(), y + getY())) {
            isIn = true;
        }
        return isIn;
    }


    Polygon createPolygon() {
        polygon = new Polygon();
        polygon.addPoint((int) line.getX1(), (int) line.getY1() + 4);
        polygon.addPoint((int) line.getX2(), (int) line.getY2() + 4);
        polygon.addPoint((int) line.getX2(), (int) line.getY2() - 4);
        polygon.addPoint((int) line.getX1(), (int) line.getY1() - 4);
        return polygon;
    }

    Polygon createPolygonFork() {
        polygon = new Polygon();
        polygon.addPoint((int) l1.getX1(), (int) l1.getY1() + 4);
        polygon.addPoint((int) l2.getX2(), (int) l2.getY2() + 4);
        polygon.addPoint((int) l1.getX2(), (int) l1.getY2() - 4);
        polygon.addPoint((int) l2.getX1(), (int) l2.getY1() - 4);
        return polygon;
    }

    public void adjustLine(int x, int y) {
        xdiff = x - ((RectangleAttachment) getBase()).getUpperRight().getX();
        ydiff = y - ((Attachment) getBase()).getGraphLocation().getY() - 15 / 2;
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        GraphicView gv = (GraphicView) this.getParent();
        gv.setLayer(this, 10, -1);
        this.adjustLine(e.getX() + this.getX(), e.getY() + this.getY());
        this.setHighlighted(true);
        if (gv.getInputFound() != null) {
            gv.notifyObservers(new SetHighlightColorCommand(Color.GREEN, this));
        } else {
            gv.notifyObservers(new SetHighlightColorCommand(oldColor, this));
        }
        gv.repaint();
    }

    @Override
    public void mousePressed(MouseEvent e) {
        this.dragStartPoint = e.getPoint();
        this.dragEndPoint = e.getPoint();
        oldColor = this.getHighlightColor();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        this.setHighlighted(false);
        GraphicView gv = (GraphicView) this.getParent();
        //check trigger
        if(e.isPopupTrigger() ){
            Point point = e.getPoint();
            point.translate(this.getX(), this.getY());
            gv.getPopup().menuOutput(this,point);
            gv.getPopup().getPopup().show(this, e.getX(), e.getY());
        }
        //end check
        GOutput op = this;
        if (gv.getInputFound() != null) {
            
           //check if there is not already made a connection to the inputport
           boolean empty = true;
           GNode gnode =  (GNode)((GInputPort)gv.getInputFound().getBase()).getBase();
           ArrayList<GConnection> conns = gnode.getConns();
           int i = 0;
           while(i < conns.size() && empty){
             if(conns.get(i).getClass().isAssignableFrom(GHandleConnection.class)){
                GHandleConnection ghc = (GHandleConnection)conns.get(i);
                GInputPort gip = ghc.getInput();
                if(((GInputPort)gv.getInputFound().getBase()).equals(gip)){
                    empty = false;
                }
             }
             i++;
           }//end of checking

           if(empty){
                GInputPort ip = (GInputPort) gv.getInputFound().getBase();
                GOutputPort gop = (GOutputPort) this.getBase();
                if (GTransmitterNode.class.isAssignableFrom(gv.getInputFound().getBase().getClass())
                && GTransmitterNode.class.isAssignableFrom(((GOutputPort) op.getBase()).getBase().getClass())) {
                    GTransmitterNode t1 = (GTransmitterNode)gv.getInputFound().getBase();
                    GTransmitterNode t2 = (GTransmitterNode) ((GOutputPort) op.getBase()).getBase();
                    if (t1.getBase() != t2.getBase()) {
                        gv.notifyObservers(new AddConnectionCommand(ip,op, this, gv.getRootPane().getLayeredPane()));
                        gv.notifyObservers(new DeleteCommand(this));
                        gv.notifyObservers(new AddAttachmentCommand(Constants.getOutputString(), gop));
                    } else {
                        JOptionPane.showMessageDialog(gv.getParent(), LabelReader.getInstance().getString("InputOutputSameParentnode"), LabelReader.getInstance().getString("Error"), JOptionPane.ERROR_MESSAGE);
                        gv.notifyObservers(new DeleteCommand(this));
                        gv.notifyObservers(new AddAttachmentCommand(Constants.getOutputString(), gop));
                    }
                } else if (gv.getInputFound().getBase() != ((GOutputPort)op.getBase()).getBase()) {
                    gv.notifyObservers(new AddConnectionCommand(ip,op, this, gv.getRootPane().getLayeredPane()));
                    gv.notifyObservers(new DeleteCommand(this));
                    gv.notifyObservers(new AddAttachmentCommand(Constants.getOutputString(), gop));
                } else {
                    JOptionPane.showMessageDialog(gv.getParent(), LabelReader.getInstance().getString("InputOutputSameParentnode"), LabelReader.getInstance().getString("Error"), JOptionPane.ERROR_MESSAGE);
                    gv.notifyObservers(new DeleteCommand(this));
                    //gv.notifyObservers(new AddAttachmentCommand(Constants.getOutputString(), ip));
                    gv.notifyObservers(new AddAttachmentCommand(Constants.getOutputString(), gop));
                }
           }else{
                 JOptionPane.showMessageDialog(gv.getParent(), LabelReader.getInstance().getString("InputOccupied"), LabelReader.getInstance().getString("Error"), JOptionPane.ERROR_MESSAGE);
           }
        }
    }
}